package com.rayson.yututu.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rayson.yututu.exception.BusinessException;
import com.rayson.yututu.exception.ErrorCodeEnum;
import com.rayson.yututu.exception.ThrowUtils;
import com.rayson.yututu.manager.FileManager;
import com.rayson.yututu.mapper.PictureMapper;
import com.rayson.yututu.model.dto.UploadPictureResult;
import com.rayson.yututu.model.entity.Picture;
import com.rayson.yututu.model.entity.Space;
import com.rayson.yututu.model.entity.User;
import com.rayson.yututu.model.enums.PictureReviewStatusEnum;
import com.rayson.yututu.model.req.picture.*;
import com.rayson.yututu.model.vo.PictureVO;
import com.rayson.yututu.model.vo.TagCategoryVO;
import com.rayson.yututu.service.PictureService;
import com.rayson.yututu.service.SpaceService;
import com.rayson.yututu.service.UserService;
import com.rayson.yututu.util.PageUtil;
import com.rayson.yututu.util.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author wulei
 * @description 针对表【picture(图片)】的数据库操作Service实现
 * @createDate 2024-12-24 22:16:47
 */
@Slf4j
@Service
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture> implements PictureService {

    private static final List<String> TAG_LIST = Arrays.asList("模板", "电商", "表情包", "素材", "海报", "kunkun", "ikun", "植物", "动物");

    private static final List<String> CATEGORY_LIST = Arrays.asList("热门", "搞笑", "生活", "高清", "艺术", "校园", "背景", "简历");

    @Resource
    private FileManager fileManager;

    @Resource
    private UserService userService;

    @Resource
    private SpaceService spaceService;

    @Resource
    private TransactionTemplate transactionTemplate;


    @Override
    public PictureVO uploadPicture(Object source, PictureUploadReq pictureUploadReq, User loginUser) {
        ThrowUtils.throwIf(Objects.isNull(loginUser), ErrorCodeEnum.NOT_LOGIN);
        // 校验空间权限
        String spaceId = pictureUploadReq.getSpaceId();
        if (StrUtil.isNotBlank(spaceId)) {
            spaceService.checkSpace(spaceId, loginUser, true);
        }
        String pictureId = pictureUploadReq.getId();
        String pictureName = pictureUploadReq.getPictureName();
        if (StringUtils.hasText(pictureId)) {
            // 更新
            Picture oldPicture = this.getById(pictureId);
            ThrowUtils.throwIf(Objects.isNull(oldPicture), ErrorCodeEnum.PARAM_ERROR, "图片不存在");
            // 判断是否有权限修改
            if (!oldPicture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
                throw new BusinessException(ErrorCodeEnum.NO_AUTH, "没有权限修改该图片");
            }
        }
        // 上传图片得到图片信息
        String uploadPathPrefix = String.format("/public/%s", loginUser.getId());
        if (StrUtil.isNotBlank(spaceId)) {
            uploadPathPrefix = String.format("/space/%s", spaceId);
        }
        UploadPictureResult uploadPictureResult = Objects.isNull(source) ?
                fileManager.uploadPictureByUrl(pictureUploadReq.getFileUrl(), uploadPathPrefix, pictureName) :
                fileManager.uploadPicture((MultipartFile) source, uploadPathPrefix, pictureName);
        Picture picture = Picture.from(uploadPictureResult);
        picture.setUserId(loginUser.getId());
        if (StringUtils.hasText(pictureId)) {
            // 更新图片
            picture.setId(pictureId);
            picture.setEditTime(new Date());
        } else {
            // 创建图片
            picture.setSpaceId(spaceId);
        }
        // 填充审核相关的字段
        this.fillReviewParam(picture, loginUser);
        // 更新空间额度
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                boolean result = saveOrUpdate(picture);
                if (result && StrUtil.isNotBlank(spaceId)) {
                    boolean update = spaceService.lambdaUpdate()
                            .eq(Space::getId, spaceId)
                            .setSql("total_size = total_size + " + picture.getPicSize())
                            .setSql("total_count = total_count + 1")
                            .update();
                    log.info("上传图片后,更新用户{}空间额度，数据库执行结果: {}", picture.getUserId(), update);
                }
            }
        });
        return PictureVO.toPictureVO(picture);
    }

    @Override
    public void deletePictureById(String id, HttpServletRequest request) {
        // 根据id查询图片
        Picture picture = getById(id);
        ThrowUtils.throwIf(Objects.isNull(picture), ErrorCodeEnum.PARAM_ERROR, "图片不存在");
        User loginUser = userService.getLoginUser(request);
        // 校验权限
        // this.checkPictureAuth(picture, loginUser);
        // 删除图片，同时释放额度
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                boolean flag = removeById(id);
                log.info("用户{}删除图片{}，结果{}", loginUser.getId(), id, flag);
                boolean update = spaceService.lambdaUpdate()
                        .eq(Space::getId, picture.getSpaceId())
                        .setSql("total_size = total_size - " + picture.getPicSize())
                        .setSql("total_count = total_count - 1")
                        .update();
                log.info("删除图片后,更新用户{}空间额度，数据库执行结果: {}", picture.getUserId(), update);
            }
        });
    }

    @Override
    public void updatePicture(PictureUpdateReq pictureUpdateReq) {
        String id = pictureUpdateReq.getId();
        Picture picture = getById(id);
        ThrowUtils.throwIf(Objects.isNull(picture), ErrorCodeEnum.PARAM_ERROR, "图片不存在");
        // 校验权限
        // this.checkPictureAuth(picture, userService.getCurrentUser());
        Picture update = new Picture();
        BeanUtils.copyProperties(pictureUpdateReq, update);
        update.setId(id);
        update.setTags(JSONUtil.toJsonStr(pictureUpdateReq.getTags()));
        update.setEditTime(new Date());
        // 填充审核相关的字段
        this.fillReviewParam(update, userService.getLoginUser(ServletUtil.getRequest()));
        boolean flag = updateById(update);
        log.info("管理员更新图片{}，结果{}", id, flag);
    }

    @Override
    public Page<Picture> listPicture(PictureQueryReq queryReq) {
        Page<Picture> page = PageUtil.getPage(queryReq);
        LambdaQueryWrapper<Picture> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasText(queryReq.getName()), Picture::getName, queryReq.getName());
        queryWrapper.like(StringUtils.hasText(queryReq.getIntroduction()), Picture::getIntroduction, queryReq.getIntroduction());
        queryWrapper.eq(StringUtils.hasText(queryReq.getCategory()), Picture::getCategory, queryReq.getCategory());
        queryWrapper.like(StringUtils.hasText(queryReq.getTag()), Picture::getTags, "%\"" + queryReq.getTag() + "\"%");
        queryWrapper.eq(Objects.nonNull(queryReq.getPicSize()), Picture::getPicSize, queryReq.getPicSize());
        queryWrapper.eq(Objects.nonNull(queryReq.getPicWidth()), Picture::getPicWidth, queryReq.getPicWidth());
        queryWrapper.eq(Objects.nonNull(queryReq.getPicHeight()), Picture::getPicHeight, queryReq.getPicHeight());
        queryWrapper.eq(StringUtils.hasText(queryReq.getPicFormat()), Picture::getPicFormat, queryReq.getPicFormat());
        queryWrapper.eq(Objects.nonNull(queryReq.getReviewStatus()), Picture::getReviewStatus, queryReq.getReviewStatus());
        queryWrapper.eq(StrUtil.isNotBlank(queryReq.getSpaceId()), Picture::getSpaceId, queryReq.getSpaceId());
        queryWrapper.isNull(queryReq.isPublic(), Picture::getSpaceId);
        queryWrapper.ge(Objects.nonNull(queryReq.getStartEditTime()), Picture::getEditTime, queryReq.getStartEditTime());
        queryWrapper.lt(Objects.nonNull(queryReq.getEndEditTime()), Picture::getEditTime, queryReq.getEndEditTime());
        if (StringUtils.hasText(queryReq.getSearchText())) {
            queryWrapper.and(i -> i.like(Picture::getName, queryReq.getSearchText()).or().like(Picture::getIntroduction, queryReq.getSearchText()));
        }
        return page(page, queryWrapper);
    }

    @Override
    public TagCategoryVO getTagCategory() {
        return TagCategoryVO.builder().tagList(TAG_LIST).categoryList(CATEGORY_LIST).build();
    }

    @Override
    public Page<PictureVO> listPictureVO(PictureQueryReq queryReq) {
        String spaceId = queryReq.getSpaceId();
        if (StrUtil.isBlank(spaceId)) {
            // 查询公共图库
            queryReq.setPublic(true);
            queryReq.setReviewStatus(PictureReviewStatusEnum.PASS.getStatus());
        } else {
            // 查询私有图库，校验权限
            spaceService.checkSpace(spaceId, userService.getCurrentUser(), false);
        }
        Page<Picture> page = listPicture(queryReq);
        List<Picture> records = page.getRecords();
        List<PictureVO> pictureVOList = records.stream().map(PictureVO::toPictureVO).collect(Collectors.toList());
        Page<PictureVO> resultPage = PageUtil.getPage(queryReq);
        resultPage.setTotal(page.getTotal());
        resultPage.setRecords(pictureVOList);
        return resultPage;
    }

    @Override
    public void reviewPicture(PictureReviewReq req, HttpServletRequest request) {
        String id = req.getId();
        Integer reviewStatus = req.getReviewStatus();
        PictureReviewStatusEnum pictureReviewStatusEnum = PictureReviewStatusEnum.getEnumByStatus(reviewStatus);
        // 1.判断审核状态值是否合法
        ThrowUtils.throwIf(Objects.isNull(pictureReviewStatusEnum), ErrorCodeEnum.PARAM_ERROR, "审核状态值不合法");
        // 2.图片是否存在
        Picture oldPicture = getById(id);
        ThrowUtils.throwIf(Objects.isNull(oldPicture), ErrorCodeEnum.PARAM_ERROR, "图片不存在");
        // 3.审核状态是否重复
        ThrowUtils.throwIf(Objects.equals(oldPicture.getReviewStatus(), reviewStatus), ErrorCodeEnum.PARAM_ERROR, "重复重复");
        // 4.更新数据库
        Picture update = new Picture();
        BeanUtils.copyProperties(req, update);
        update.setReviewTime(new Date());
        User loginUser = userService.getLoginUser(request);
        update.setReviewerId(loginUser.getId());
        boolean result = updateById(update);
        log.info("{}审核图片{}，审核结果：{}", loginUser.getId(), id, pictureReviewStatusEnum.getDesc());
        log.info("审核图片{}修改数据库结果: {}", id, result);
    }

    @Override
    public void fillReviewParam(Picture picture, User loginUser) {
        if (userService.isAdmin(loginUser)) {
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getStatus());
            picture.setReviewerId(loginUser.getId());
            picture.setReviewTime(new Date());
            picture.setReviewMsg("管理员自动过审");
        } else {
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getStatus());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer getPictureFromInternet(PictureBatchReq pictureBatchReq, User loginUser) {
        String keyword = pictureBatchReq.getKeyword();
        Integer total = pictureBatchReq.getNum();
        String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s", keyword);
        Document document;
        try {
            document = Jsoup.connect(fetchUrl).get();
        } catch (IOException e) {
            log.error("网络抓取图片异常", e);
            throw new BusinessException(ErrorCodeEnum.SYSTEM_ERROR, "获取图片失败");
        }
        Element div = document.getElementsByClass("dgControl").first();
        if (ObjectUtil.isNull(div)) {
            throw new BusinessException(ErrorCodeEnum.SYSTEM_ERROR, "获取元素失败");
        }
        Elements imgList = div.select("img.mimg");
        int uploadCount = 0;
        for (Element element : imgList) {
            String imgUrl = element.attr("src");
            if (StrUtil.isBlank(imgUrl)) {
                log.info("图片地址为空");
                continue;
            }
            if (!imgUrl.startsWith("http")) {
                log.info("图片URL不合法: {}", imgUrl);
                continue;
            }
            // 处理url
            int index = imgUrl.indexOf("?");
            if (index > 0) {
                imgUrl = imgUrl.substring(0, index);
            }
            PictureUploadReq uploadReq = new PictureUploadReq();
            uploadReq.setFileUrl(imgUrl);
            uploadReq.setPictureName(keyword + "-" + uploadCount + 1);
            try {
                PictureVO pictureVO = this.uploadPicture(null, uploadReq, loginUser);
                log.info("图片上传成功,id={}", pictureVO.getId());
                uploadCount++;
            } catch (Exception e) {
                log.error("上传图片失败", e);
            }
            if (uploadCount >= total) {
                break;
            }
        }
        return uploadCount;
    }

    @Override
    public void checkPictureAuth(Picture picture, User loginUser) {
        String spaceId = picture.getSpaceId();
        if (StrUtil.isBlank(spaceId)) {
            // 公共图库只有本人和管理员才能修改
            if (!userService.isAdmin(loginUser) && !loginUser.getId().equals(picture.getUserId())) {
                throw new BusinessException(ErrorCodeEnum.NO_AUTH);
            }
        } else {
            // 私有空间只有本人才能修改
            if (!loginUser.getId().equals(picture.getUserId())) {
                throw new BusinessException(ErrorCodeEnum.NO_AUTH);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchEditPicture(BatchEditPictureReq batchEditPictureReq, User loginUser) {
        List<String> idList = batchEditPictureReq.getPictureIdList();
        String spaceId = batchEditPictureReq.getSpaceId();
        Space space = spaceService.getById(spaceId);
        ThrowUtils.throwIf(Objects.isNull(space), ErrorCodeEnum.PARAM_ERROR, "空间不存在");
        if (!StrUtil.equals(space.getUserId(), loginUser.getId())) {
            throw new BusinessException(ErrorCodeEnum.NO_AUTH, "没有权限修改该空间内的图片");
        }
        // 根据id查询图片
        List<Picture> pictureList = lambdaQuery()
                .select(Picture::getId, Picture::getSpaceId)
                .in(Picture::getId, idList)
                .eq(Picture::getSpaceId, spaceId)
                .list();
        if (pictureList.isEmpty()) {
            return;
        }
        String category = batchEditPictureReq.getCategory();
        List<String> tags = batchEditPictureReq.getTags();
        pictureList.forEach(picture -> {
            if (StrUtil.isNotBlank(category)) {
                picture.setCategory(category);
            }
            if (CollUtil.isNotEmpty(tags)) {
                picture.setTags(JSONUtil.toJsonStr(tags));
            }
        });
        // 批量重命名
        String nameRule = batchEditPictureReq.getNameRule();
        executeNameRule(pictureList, nameRule);
        // 执行更新操作
        boolean result = updateBatchById(pictureList);
        log.info("批量修改图片结果: {}", result);
    }

    private void executeNameRule(List<Picture> pictureList, String nameRule) {
        if (StrUtil.isBlank(nameRule) || CollUtil.isEmpty(pictureList)) {
            return;
        }
        long count = 1;
        try {
            for (Picture picture : pictureList) {
                String newName = nameRule.replaceAll("\\{序号}", String.valueOf(count++));
                picture.setName(newName);
            }
        } catch (Exception e) {
            log.error("执行命名规则失败", e);
            throw new BusinessException(ErrorCodeEnum.OPERATION_ERROR, "执行命名规则失败");
        }
    }
}




